
package ProOF.apl.methods.GA.population;

import ProOF.apl.methods.GA.population.aPopulation.OperatorSet;
import ProOF.apl.problems.real.ExceptionForceReinit;
import ProOF.com.LinkerParameters;
import ProOF.com.LinkerResults;
import ProOF.com.runner.ExceptionForceFinish;
import ProOF.gen.operator.Crossover;
import ProOF.gen.operator.oCrossover;
import ProOF.gen.operator.oMutation;
import ProOF.opt.abst.problem.meta.Problem;
import ProOF.opt.abst.problem.meta.Solution;
import ProOF.opt.abst.problem.meta.codification.Codification;
import ProOF.opt.abst.problem.meta.objective.SingleObjective;

import java.util.Collections;

/**
 *
 * @author Hossomi
 */
public class PopulationOpEvolve extends PopulationRoulette {

    @Override
    public void start() throws Exception {
        super.start();
        opSet = new OperatorSetEvolve(problem, crossover, mutation);
    }
    
    @Override
	public void evolve() throws Exception {
        Solution child;
        Solution parents[];
        Codification codif;
        boolean add;
        boolean mutation = false;
        boolean crossover = false;
        
        System.out.println("Starting evolution...");
        
        // Start evolution
        try {
            while (!stop.end()) {

                // Select parents
                parents = select();
                add = false;

                // Try crossover
                if (problem.rmd.nextDouble() < crossRate) {
                    child = opSet.crossover( parents[0], parents[1] );
                    add = true;
                    crossover = true;
                }
                else {
                    codif = problem.NewCodification();
                    codif.Copy(problem, parents[0].codif());
                    child = problem.NewSolution(codif);
                }

                // Try mutation
                if (problem.rmd.nextDouble() < mutRate) {
                    opSet.mutation(child);
                    add = true;
                    mutation = true;
                }

                // Only try to add transformed individuals
                // Population convergence is not interesting here
                if (add) {
                    // Evaluate
                    problem.evaluate(child);
                    ((OperatorSetEvolve)opSet).opEvolve(child, parents[0], parents[1], crossover, mutation);

                    // Try to insert
                    if (insert(child)) {
                        organize();
                    }
                }
            }
        }
        catch (ExceptionForceReinit e) {
            reinitialize();
            evolve();
        }
	}
    
	@Override
	public String name() {
		return "Pop. Oper. Evolve";
	}	

	protected class OperatorSetEvolve extends OperatorSet{
		
	    private   int    crossoverWeight[];
	    private   int    mutationWeight[];
	    private   int    lastCrossover;
	    private   int    lastMutation;
		
		protected int selectCrossover(){
			int max = 0;
			int sum = 0;
			int val;
			int i;
			for(i = 0; i < crossoverWeight.length; i++)
				max += crossoverWeight[i];
			val = problem.rmd.nextInt(max);
			for(i = 0; i < crossoverWeight.length; i++){
				sum += crossoverWeight[i];
				if(val < sum){
					return i;
				}
			}
			return 0;
		}
		
		protected int selectMutation(){
			int max = 0;
			int sum = 0;
			int val;
			int i;
			for(i = 0; i < mutationWeight.length; i++)
				max += mutationWeight[i];
			val = problem.rmd.nextInt(max);
			for(i = 0; i < mutationWeight.length; i++){
				sum += mutationWeight[i];
				if(val < sum){
					return i;
				}
			}
			return 0;
		}
		
        public OperatorSetEvolve( Problem problem, oCrossover crossover[], oMutation mutation[] ) {
        	super(problem, crossover, mutation);
            
            crossoverWeight = new int[crossover.length];
            for(int i = 0; i < crossoverWeight.length; i++)
            	crossoverWeight[i] = 1;
            
            mutationWeight = new int[mutation.length];
            for(int i = 0; i < mutationWeight.length; i++)
            	mutationWeight[i] = 1;
            
        }
        
        public void opEvolve(Solution child, Solution ind1, Solution ind2, boolean crossover, boolean mutation){
            if(child.LE(ind1) && child.LE(ind2)){
            	if(crossover)
            		crossoverWeight[lastCrossover]++;
            	if(mutation)
            		mutationWeight[lastMutation]++;
            }else if(child.GE(ind1) && child.GE(ind2)){
            	if(crossover && crossoverWeight[lastCrossover] > 1)
            		crossoverWeight[lastCrossover]--;
            	if(mutation && mutationWeight[lastMutation] > 1)
            		mutationWeight[lastMutation]--;
            }
        }
        
        @Override
        public void results( LinkerResults link ) throws Exception {
            String cw = "", mw = "";
            for (int w: crossoverWeight)
                cw += w + "/";
            
            for (int w: mutationWeight)
                mw += w + "/";
            
            link.writeString("Cross Weight", cw);
            link.writeString("Mut Weight", mw);
        }
        
        @Override
        public Solution crossover( Solution ind1, Solution ind2 ) throws Exception {
            lastCrossover = selectCrossover();
            return problem.NewSolution(crossover[lastCrossover].crossover( problem, ind1.codif(), ind2.codif() ));
        }
        
        @Override
        public void mutation( Solution ind ) throws Exception {
            lastMutation = selectMutation();
            mutation[lastMutation].mutation(problem, ind.codif());
        }
	}
}
